package srzy.diameter

import scala.annotation.tailrec
import srzy.common.packunpack
import srzy.common.Logging

object BytesToCompareLog extends Logging {
  import srzy.diameter.DataFormat._

  def dccBytesToCompareLog(arr: Array[Byte]): String = {
    val l = arr.toList
    val header = l.slice(0, dccHeaderLength)
    val body = l.slice(dccHeaderLength, l.length)
    val dccHeaderInfo = dccHeaderFieldsInfo.map(x => {
      val e1 = x._1
      val e2 = x._2
      (e1, packunpack.unpackLong(header.toArray, e2._1, e2._2))
    })

    val dccName = dccHeaderDict.find(x => {
      val l = x._2.map(e => if (e._1 == "flags")
        (e._1, getRequestFlag(e._2.toLong))
      else
        (e._1, e._2.toLong))
      val dccHeaderInfoTmp = dccHeaderInfo.map(e => if (e._1 == "flags")
        (e._1, getRequestFlag(e._2.toLong))
      else
        (e._1, e._2.toLong))
      l.map(t => dccHeaderInfoTmp.exists(q => q == t)).forall(x => x == true)

      //      l.map(t => dccHeaderInfo.exists(q => q == t)).forall(x => x == true)
    }) match {
      case Some(v) => v._1
      case None => {
        val dccHeaderInfoTmp = dccHeaderInfo.map(e => if (e._1 == "flags")
          (e._1, getRequestFlag(e._2.toLong))
        else
          (e._1, e._2.toLong))
        val dccHeaderDictTmp = dccHeaderDict.map(q => (q._1, q._2.map(e => if (e._1 == "flags")
          (e._1, getRequestFlag(e._2.toLong))
        else
          (e._1, e._2.toLong))))
        logger.error("dccHeaderInfoTmp:\n" + dccHeaderInfoTmp.mkString("\n"))
        logger.error("dccHeaderDictTmp:" + dccHeaderDictTmp.mkString("\n"))
        throw new Exception("dccHeaderDict not contain field " + dccHeaderInfo)
        //        logger.error("dccHeaderInfo:" + dccHeaderInfo)
        //        throw new Exception("dccHeaderDict not contain field")
      }
    }
    //    dccHeaderInfoFormat(dccName, dccHeaderInfo) + "\n" +
    "[" + dccName + "]" + "\n" +
      avpsBytesToCompareLog(body.toArray)
  }

  def dccHeaderInfoFormat(name: String, l: List[(String, Long)]) = {
    name + "\n" +
      l.map(x => "[" + x._1 + "] = [ " + x._2 + "]").mkString("\n")
  }

  def splitAVPs(arr: Array[Byte]) = {
    def getAVPLength(arr: Array[Byte]) = {
      val lengthInfo = withOptionAVPHeaderFieldsInfoMap.get(avpHeaderLengthText).get
      packunpack.unpackLong(arr.toArray, lengthInfo._1, lengthInfo._2).toInt
    }
    def getAVPField(arr: Array[Byte]): (Array[Byte], Array[Byte]) = {
      arr.splitAt(padLength(getAVPLength(arr)))
    }
    @tailrec def f(l: List[Array[Byte]], arr: Array[Byte]): List[Array[Byte]] = {
      if (arr.length == 0) {
        l
      } else {
        val (a1, a2) = getAVPField(arr)
        f(l ::: a1 :: Nil, a2)
      }
    }
    f(List.empty[Array[Byte]], arr)
  }

  def avpsBytesToCompareLog(arr: Array[Byte]) = {
    splitAVPs(arr).map(avpBytesToCompareLog(_)).mkString("\n")
  }

  def avpBytesToCompareLog(arr: Array[Byte]): String = {
    val lengthInfo = withOptionAVPHeaderFieldsInfoMap.get(avpHeaderLengthText).get
    val length = packunpack.unpackLong(arr.toArray, lengthInfo._1, lengthInfo._2)

    val flagInfo = withOptionAVPHeaderFieldsInfoMap.get(option._4).get
    val flag = packunpack.unpackLong(arr.toArray, flagInfo._1, flagInfo._2)

    val optionFlag = { flag >= option._5.toLong } //true if with option

    val headerLength = if (optionFlag) {
      withOptionAVPHeaderLength
    } else {
      noOptionAVPHeaderLength
    }

    val body = arr.slice(headerLength, length.toInt)
    //    println("headerLength:" + headerLength)
    val avpHeaderFields = (if (optionFlag) {
      withOptionAVPHeaderFieldsInfo.map(x => {
        val name = x._1
        val offset = x._2._1
        val digit = x._2._2
        (name, packunpack.unpackLong(arr, offset, digit))
      })
    } else {
      noOptionAVPHeaderFieldsInfo.map(x => {
        val name = x._1
        val offset = x._2._1
        val digit = x._2._2
        (name, packunpack.unpackLong(arr, offset, digit))
      })
    }).filter(_._1 != avpHeaderLengthText)

    val avpCode = avpHeaderFields.toMap.apply(avpHeaderType)
    val (avpName, dataType) = avpDict.find(x => x._2.toLong == avpCode) match {
      case Some(v) => (v._1, v._3)
      case None => throw new Exception("avpCode :" + avpCode + " not found")
    }

    "[" + avpName + avpHeaderFields.map(x => "(" + x._1 + " = " + x._2 + ")").mkString("") + "] = [" +
      (dataType match {
        case "Int" => packunpack.unpackInt(body, 0)
        case "Long" => packunpack.unpackLong(body, 0)
        case "UnsignedInt" => packunpack.unpackLong(Array.fill(4)(0.asInstanceOf[Byte]) ++ body ++ Nil, 0)
        case "String" | "UTF8String" => {
          val s = new String(body)
          """[^\p{Print}]""".r.findFirstIn(s) match {
            case Some(v) => "0x" + body.toHexString
            case None => s
          }
        }
        case "Grouped" => "\n" +
          avpsBytesToCompareLog(body).split("\n").toList.map("| " + _).mkString("\n") + "\n"
      }) + "]"

  }
}